import java.io.*;
import java.net.*;

/*Parametri da passare: DatagramSocket di aknownledgment
 * 						array di celle da modificare
 * 						riceve massimo una array di 100000		
 * 
 */
public class Controller{
	
		//ATTRIBUTI CLASSE 
		DatagramSocket serverCR;
		Packet pck;
		int L;
		int Lr;
		int N;
		Packet[] tabella;
		DatagramSocket ACKsocket;
		InetAddress IPChannel;
		

		//METODI CLASSE
		public Controller(DatagramSocket receiverSocket, Packet[] tabella, Packet firstMessage, int N, int Lr, DatagramSocket ACKsocket, InetAddress IPChannel) {
				this.serverCR = receiverSocket;
				this.pck = firstMessage;
				this.tabella =tabella;
				this.L = firstMessage.toByte().length;
				this.N = N;
				this.Lr =Lr;
				this.ACKsocket =ACKsocket;
				this.IPChannel = IPChannel;
				
		}
		
		public void runCR() {
			
			System.out.println("Controller started");
			byte[] receiveData = new byte[this.L];
			tabella[this.pck.getIDPacket()]=pck;
			ParallelCR parallelCR = new ParallelCR(pck, this.tabella, this.N, this.Lr, this.ACKsocket, this.IPChannel);
			Thread thread = new Thread(parallelCR);
			thread.run();
			boolean finito = false;
			while(!finito) {
				try {
					DatagramPacket receivedPacket = new DatagramPacket(receiveData, receiveData.length);
					this.serverCR.receive(receivedPacket);
					Packet rec = new Packet(receivedPacket.getData());
					parallelCR = new ParallelCR(rec, this.tabella, this.N, this.Lr, this.ACKsocket, this.IPChannel);
					Thread t = new Thread(parallelCR);
					t.run();
					if (parallelCR.fine == true)
						finito = true;
				} catch(IOException e) {
					e.printStackTrace();
				}
			}
		}
		
		
		private class ParallelCR implements Runnable {
			
			private Packet rec;
			private Packet[] tabella;
			private int N;
			private int Lr;
			private DatagramSocket ACKsocket;
			private InetAddress IPChannel;
			public boolean fine;
			
			//METODO COSTRUTTORE
			public ParallelCR(Packet client, Packet[] tabella, int N, int Lr,DatagramSocket ACKsocket, InetAddress IPChannel){	
				this.rec = client;
				this.tabella = tabella;
				this.N = N;
				this.Lr = Lr;
				this.ACKsocket = ACKsocket;
				this.IPChannel = IPChannel;
				this.fine = false;
			}
			
			public int mod(){
				for (int i=0;i<N;i++){
					if (tabella[i] == null)
						return i;
				}
				return 65535;
			}
			
			public void run(){                                       
				try {	
					int ID = -3;
					long tries = 10000;
					if (rec.getIDPacket() == 65535) {
						System.out.println("E' arrivato ultimo");
						ID = mod();
						this.fine = true;
						tries = 200000;
						
					}else{
						ID = rec.getIDPacket();
						System.out.println("E' arrivato:"+ rec.getIDPacket());
					}
					if (ID != 65535)
						this.tabella[ID] = rec;	
					byte[] vuoto = new byte[this.Lr];
					System.out.println("MANDO ACK: "+rec.getIDPacket());
					Packet ack = new Packet(rec.getIPSource(), 65435, rec.getIPDestination(), 65434, rec.getIDPacket(), vuoto);
					DatagramPacket sendPacket = new DatagramPacket(ack.toByte(), ack.getLength(), this.IPChannel , 65432);
					for (int i=0; i < tries; i++){  
						//System.out.println(i);//Numero di ack da rimandare consecutivamente
						this.ACKsocket.send(sendPacket);
					}	
					
					
				} catch(Exception e) {
	    			e.printStackTrace();
				  }
	        }
		}
}